/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004-2005  Benoit Blanchon                      */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/


/******************************************************************/
/* Build configuration                                            */
/******************************************************************/

#define	TRACE_LEVEL	2


/******************************************************************/
/* Includes                                                       */
/******************************************************************/

// module's interface
#include "Condition.h"

// standards headers
#include <windows.h>
#include <shlwapi.h>
#include <tchar.h>

// project's headers
#include "Assert.h"
#include "Reason.h"
#include "Strlcpy.h"
#include "Trace.h"
#include "Wildcards.h"


/******************************************************************/
/* Exported function :                                            */
/******************************************************************/

LPCONDITION Condition_AllocAndSet (UINT nReason, LPCTSTR szFormat, ...) 
{
  CONDITION * pCond ;
  va_list va ;
  
  va_start (va, szFormat) ;
  
  pCond = Condition_AllocAndSetV (nReason, szFormat, va) ;

  va_end (va) ;

  return pCond ;
}


/******************************************************************/
/* Exported function :                                            */
/******************************************************************/

LPCONDITION Condition_AllocAndSetV (UINT nReason, LPCTSTR szFormat, va_list va) 
{
  CONDITION * pCond ;
  UINT nSize ;

  // calc needed size
  nSize = Condition_CalcTotalSizeV (szFormat, va) ;

  // alloc
  pCond = (CONDITION*) malloc (nSize) ;

  // alloc failed ?
  if( ! pCond ) return NULL ;

  // fill size field
  pCond->nSize = nSize ;

  // set content
  if( ! Condition_SetV(pCond,nReason,szFormat,va) )
    {
      free (pCond) ;
      return NULL ;
    }

  return pCond ;
}


/******************************************************************/
/* Exported function :                                            */
/******************************************************************/

UINT Condition_CalcDataSizeV (LPCTSTR szFormat, va_list va) 
{
  int iParam, nParams ;
  int nDataSize ;
  LPCTSTR szCurValue ;

  // start with the CONDITION structure size
  nDataSize = 0 ;

  // count params
  nParams = _tcslen (szFormat) ;
  ASSERT (nParams<MAX_PARAMS) ;

  // for each param (begin)
  for( iParam=0 ; iParam<nParams ; iParam++ )
    {
      switch( szFormat[iParam] )
	{	  
	case '*': // PARAM_ANY
	  nDataSize += 2*sizeof(UINT) ;
	  break ;

	case 'n': // PARAM_UINT
	  va_arg (va, unsigned) ;
	  nDataSize += 3 * sizeof(UINT) ;
	  break ;

	case 's': // PARAM_STRING
	case 'w': // PARAM_WILDCARDS
	case 'p': // PARAM_PATH
	  szCurValue = va_arg(va,LPTSTR) ;
	  nDataSize += 2*sizeof(UINT) + ((szCurValue?_tcslen(szCurValue):0)+1)*sizeof(TCHAR) ;
	  break ;
	  
	default:
	  ASSERT (!"[Invalid condition format]") ;
	}
    }
  // for each param (end)

  return nDataSize ;
}


/******************************************************************/
/* Exported function :                                            */
/******************************************************************/

UINT Condition_CalcTotalSizeV (LPCTSTR szFormat, va_list varg) 
{
  return Condition_CalcDataSizeV(szFormat,varg) + sizeof(CONDITION) - sizeof(BYTE) ;
}


/******************************************************************/
/* Exported function :                                            */
/******************************************************************/

BOOL Condition_Check (LPCCONDITION pCondToMatch, LPCCONDITION pCond) 
{
  UINT			iParam, nParams ;
  CONDITION_PARAM	*pParamToMatch, *pParam ;
  BOOL			bParamMatch ;

  // check if reasons are equals
  if( pCondToMatch->nReason != pCond->nReason )
    return FALSE ;

  // check if params count are equals
  if( pCondToMatch->nParams != pCond->nParams )
    return FALSE ;

  nParams = pCondToMatch->nParams ;

  // for each param (begin)
  for( iParam=0 ; iParam<nParams ; iParam++ )
    {
      pParamToMatch = (CONDITION_PARAM*)( pCondToMatch->data + pCondToMatch->nParamsOffs[iParam] ) ;
      pParam = (CONDITION_PARAM*)( pCond->data + pCond->nParamsOffs[iParam] ) ;

      switch( pParamToMatch->nType )
	{
	case PARAM_ANY:	  
	  bParamMatch = TRUE ;
	  break ;
	  
	case PARAM_UINT:
	  if( pParam->nType == pParamToMatch->nType )
	    bParamMatch = pParamToMatch->nValue == pParam->nValue ;	    
	  else bParamMatch = FALSE ;
	  break ;
	  
	case PARAM_STRING:
	  if( pParam->nType == pParamToMatch->nType )
	    bParamMatch = !_tcsicmp(pParamToMatch->szValue, pParam->szValue) ;
	  else bParamMatch = FALSE ;
	  break ;

	case PARAM_WILDCARDS:
	  switch( pParam->nType )
	    {
	    case PARAM_STRING:
	      bParamMatch = Wildcards_CompleteStringCmp(pParamToMatch->szValue, pParam->szValue) ;
	      break ;
	    case PARAM_WILDCARDS:
	    case PARAM_PATH:
	      bParamMatch = !_tcsicmp(pParamToMatch->szValue, pParam->szValue) ;
	      break ;
	    default:
	      bParamMatch = FALSE ;
	    }
	  break ;

	case PARAM_PATH:
	  switch( pParam->nType )
	    {
	    case PARAM_STRING:
	      bParamMatch = Wildcards_CompletePathCmp(pParamToMatch->szValue, pParam->szValue) ;
	      // used to be: bParamMatch = PathMatchSpec (pParam->szValue, pParamToMatch->szValue) ;
	      break ;
	    case PARAM_WILDCARDS:
	    case PARAM_PATH:
	      bParamMatch = !_tcsicmp(pParamToMatch->szValue, pParam->szValue) ;
	      break ;
	    default:
	      bParamMatch = FALSE ;
	    }
	  break ;

	default:
	  ASSERT (!"[Invalid param type]") ;
	}

      // stop as soon as one param doesn't match
      if( ! bParamMatch ) return FALSE ;
    }
  // for each param (end)

  return TRUE ;
}


/******************************************************************/
/* Exported function : DlgProc                                    */
/******************************************************************/

LPCONDITION Condition_Dup (LPCCONDITION pSrc) 
{
  CONDITION * pDst ;

  ASSERT (pSrc!=NULL) ;
  ASSERT (pSrc->nSize!=0) ;
  
  // alloc new condition
  pDst = malloc (pSrc->nSize) ;
  if( ! pDst ) return NULL ;

  // copy
  memcpy (pDst, pSrc, pSrc->nSize) ;

  return pDst ;
}


/******************************************************************/
/* Exported function : DlgProc                                    */
/******************************************************************/

UINT Condition_GetParamAsString (LPCCONDITION pCond, UINT iParam, LPTSTR szBuffer, UINT nSize) 
{
  CONDITION_PARAM * pParam ;

  // verify params
  ASSERT (pCond!=NULL) ;

  if( iParam >= pCond->nParams )
    {
      szBuffer[0] = 0 ;
      return 0 ;
    }
    
  pParam = (CONDITION_PARAM*)( pCond->data + pCond->nParamsOffs[iParam] ) ;

  switch( pParam->nType )
    {
    case PARAM_ANY:
      _tcslcpy (szBuffer, TEXT("*"), nSize) ;
      break ;

    case PARAM_UINT:
      wsprintf (szBuffer, TEXT("%u"), pParam->nValue) ;
      break ;

    case PARAM_STRING:
    case PARAM_WILDCARDS:
    case PARAM_PATH:
      _tcslcpy (szBuffer, pParam->szValue, nSize) ;
      break ;

    default:
      _tcslcpy (szBuffer, TEXT("[Invalid param type]"), nSize) ;
    }

  TRACE_INFO (TEXT("Param %d : %s\n"), iParam, szBuffer) ;

  return _tcslen(szBuffer) ;
}


/******************************************************************/
/* Exported function : DlgProc                                    */
/******************************************************************/

UINT Condition_GetParamCount (LPCCONDITION pCond) 
{
  // verify params
  ASSERT (pCond!=NULL) ;
  
  return pCond->nParams ;
}


/******************************************************************/
/* Exported function : DlgProc                                    */
/******************************************************************/

LPCTSTR Condition_GetParamString (LPCCONDITION pCond, UINT iParam) 
{
  CONDITION_PARAM * pParam ;

  // verify params
  ASSERT (pCond!=NULL) ;
  ASSERT (iParam>=0) ;
  ASSERT (iParam<pCond->nParams) ;
    
  pParam = (CONDITION_PARAM*)( pCond->data + pCond->nParamsOffs[iParam] ) ;

  ASSERT (pParam->nType==PARAM_STRING || pParam->nType==PARAM_WILDCARDS || pParam->nType==PARAM_PATH) ;

  return pParam->szValue ;
}


/******************************************************************/
/* Exported function : DlgProc                                    */
/******************************************************************/

UINT Condition_GetParamType (LPCCONDITION pCond, UINT iParam) 
{
  CONDITION_PARAM * pParam ;

  // verify params
  ASSERT (pCond!=NULL) ;
  ASSERT (iParam>=0) ;
  ASSERT (iParam<pCond->nParams) ;
    
  pParam = (CONDITION_PARAM*)( pCond->data + pCond->nParamsOffs[iParam] ) ;

  return pParam->nType ;
}


/******************************************************************/
/* Exported function : DlgProc                                    */
/******************************************************************/

UINT Condition_GetParamUint (LPCCONDITION pCond, UINT iParam) 
{
  CONDITION_PARAM * pParam ;

  // verify params
  ASSERT (pCond!=NULL) ;
  ASSERT (iParam>=0) ;
  ASSERT (iParam<pCond->nParams) ;
    
  pParam = (CONDITION_PARAM*)( pCond->data + pCond->nParamsOffs[iParam] ) ;

  ASSERT (pParam->nType==PARAM_UINT) ;

  return pParam->nValue ;
}


/******************************************************************/
/* Exported function : DlgProc                                    */
/******************************************************************/

UINT Condition_GetReasonAsString (LPCCONDITION pCond, LPTSTR szBuffer, UINT nSize) 
{
  // verify params
  ASSERT (pCond!=NULL) ;
  ASSERT (szBuffer!=NULL) ;

  _tcslcpy (szBuffer, Reason_GetName(pCond->nReason), nSize) ;

  return _tcslen(szBuffer) ;
}


/******************************************************************/
/* Exported function : DlgProc                                    */
/******************************************************************/

BOOL Condition_SetV (LPCONDITION pCond, UINT nReason, LPCTSTR szFormat, va_list va) 
{
  UINT iParam ;
  UINT nTotalSize ;

  UINT	nCurParamOff = 0 ;
  UINT	nCurParamSize ;
  UINT	nCurValue ;	
  CONDITION_PARAM *pCurParam ;
  LPTSTR szCurValue ;

  // start with the CONDITION structure size
  nTotalSize = sizeof(CONDITION) - sizeof(BYTE) ;
  if( nTotalSize > pCond->nSize ) return FALSE ;

  pCond->nReason = nReason ;
  pCond->nParams = _tcslen (szFormat) ;

  ASSERT (pCond->nParams<MAX_PARAMS) ;
  // TO BE REMOVED :
  ASSERT (pCond->nSize>=Condition_CalcTotalSizeV(szFormat,va)) ;

  // for each param (begin)
  for( iParam=0 ; iParam<pCond->nParams ; iParam++ )
    {
      switch( szFormat[iParam] )
	{
	case 'n':

	  // read value
	  nCurValue = va_arg(va, unsigned) ; 

	  TRACE_INFO (TEXT("Param %d : %d\n"), iParam, nCurValue) ;

	  // calc size
	  nCurParamSize = 3 * sizeof(UINT) ;
	  nTotalSize +=  nCurParamSize ;
	  if( nTotalSize > pCond->nSize ) return FALSE ;
	  
	  // add entry
	  pCond->nParamsOffs[iParam] = nCurParamOff ;
	  pCurParam = (CONDITION_PARAM *)( pCond->data + nCurParamOff ) ;
	  nCurParamOff +=  nCurParamSize ;

	  // fill PARAM
	  pCurParam->nSize	= nCurParamSize ;
	  pCurParam->nType	= PARAM_UINT ;
	  pCurParam->nValue	= nCurValue ;

	  break ;

	case 's':
	case 'w':
	case 'p':

	  // read value
	  szCurValue = va_arg(va, LPTSTR) ; 

	  TRACE_INFO (TEXT("Param %d : %s\n"), iParam, szCurValue) ;

	  // calc size
	  nCurParamSize = 2*sizeof(UINT) + ((szCurValue?_tcslen(szCurValue):0)+1)*sizeof(TCHAR) ;
	  nTotalSize +=  nCurParamSize ;
	  if( nTotalSize > pCond->nSize ) return FALSE ;
	  
	  // add entry
	  pCond->nParamsOffs[iParam] = nCurParamOff ;
	  pCurParam = (CONDITION_PARAM *)( pCond->data + nCurParamOff ) ;
	  nCurParamOff +=  nCurParamSize ;

	  // fill PARAM
	  pCurParam->nSize	= nCurParamSize ;
	  pCurParam->nType	= 
	    szFormat[iParam]==TEXT('w') ? PARAM_WILDCARDS :
	    szFormat[iParam]==TEXT('p') ? PARAM_PATH : PARAM_STRING ;

	  if( szCurValue ) _tcscpy (pCurParam->szValue, szCurValue) ; 	  
	  else pCurParam->szValue[0] = 0 ;	  

	  break ;
	  
	case '*':

	  TRACE_INFO (TEXT("Param %d : *\n"), iParam) ;

	  // calc size
	  nCurParamSize = 2 * sizeof(UINT) ;
	  nTotalSize +=  nCurParamSize ;
	  if( nTotalSize > pCond->nSize ) return FALSE ;
	  
	  // add entry
	  pCond->nParamsOffs[iParam] = nCurParamOff ;
	  pCurParam = (CONDITION_PARAM *)( pCond->data + nCurParamOff ) ;
	  nCurParamOff +=  nCurParamSize ;

	  // fill PARAM
	  pCurParam->nSize	= nCurParamSize ;
	  pCurParam->nType	= PARAM_ANY ;

	  break ;

	default:
	  ASSERT (!"Invalid condition format") ;
	}
    }
  // for each param (end)

  return TRUE ;
}


/******************************************************************/
/* Exported function : DlgProc                                    */
/******************************************************************/

UINT Condition_ToString (LPCCONDITION pCond, LPTSTR szBuffer, UINT nSize) 
{
  UINT i, n ;

  n = Condition_GetReasonAsString (pCond, szBuffer, nSize) ;

  _tcsncat (szBuffer+n, TEXT(" ("), nSize-n) ;
  n += 2 ;
  
  for( i=0 ; i<pCond->nParams ; i++ )
    {
      n += Condition_GetParamAsString (pCond, i, szBuffer+n, nSize-n) ;

      if( i+1<pCond->nParams ) {
	_tcsncat (szBuffer+n, TEXT(", "), nSize-n) ;
	n += 2 ;
      }
    }

  _tcsncat (szBuffer+n, TEXT(")"), nSize-n) ;
  n += 1 ;

  return n ;
}
  

BOOL Condition_TestPartialKey (LPCCONDITION p, LPCTSTR szPartialKey) 
{
  CONDITION_PARAM	*pParam ;
  BOOL			bResult ;
  
  TRACE ;

  // verify params
  ASSERT (p!=NULL) ;
  ASSERT (szPartialKey!=NULL) ;

  // this is only valid for registry functions
  if( p->nReason != REASON_REG_SETVALUE )
    return FALSE ;

  pParam = (CONDITION_PARAM*) p->data ;

  switch( pParam->nType )
    {
    case PARAM_ANY:
      bResult = FALSE ; // no need to keep key
      break ;
    case PARAM_UINT:
      bResult = FALSE ;
      break ;
    case PARAM_STRING:
      bResult = 0==_tcsncmp(pParam->szValue,szPartialKey,_tcslen(szPartialKey)) ;
      break ;
    case PARAM_WILDCARDS:
      bResult = Wildcards_PartialStringCmp (pParam->szValue, szPartialKey) ;
      break ;
    case PARAM_PATH:
      bResult = Wildcards_PartialPathCmp (pParam->szValue, szPartialKey) ;
      break ;
    default:
      ASSERT (0) ;
    }

  return bResult ;
}
